home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
SMTPCLI.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-10-03
|
19KB
|
835 lines
/*
* CLIENT routines for Simple Mail Transfer Protocol ala RFC821
* A.D. Barksdale Garbee II, aka Bdale, N3EUA
* Copyright 1986 Bdale Garbee, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
* Modified 14 June 1987 by P. Karn for symbolic target addresses,
* also rebuilt locking mechanism
* Copyright 1987 1988 David Trulli, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
*/
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <setjmp.h>
#include <ctype.h>
#include <sys/stat.h>
#ifdef __TURBOC__
#include <dir.h>
#include <io.h>
#endif
#include "global.h"
#include "config.h"
#ifdef ANSIPROTO
#include <stdarg.h>
#endif
#include "cmdparse.h"
#include "proc.h"
#include "socket.h"
#ifdef LZW
#include "lzw.h"
#endif
#include "timer.h"
#include "netuser.h"
#include "smtp.h"
#include "dirutil.h"
#include "commands.h"
#include "session.h"
#include "files.h"
#include "event.h"
#include "server.h"
#undef SMTPTRACE 1
int16 Smtpquiet = 0;
int Smtpmode = ROUTE;
int Smtpbbs = BBS;
static int Smtpsessions = 0; /* number of client connections */
static int16 Smtpmaxcli = MAXSESSIONS; /* the max client connections allowed */
static struct smtpcli *Smtpcli = NULLSMTPCLI;
#ifdef SMTPTRACE
static int Smtptrace = FALSE; /* used for trace level */
#endif
#ifdef LZW
int Smtplzw = TRUE;
static int Smtpbatch = TRUE;
#else
static int Smtpbatch = FALSE;
#endif
static void near
del_job(struct smtp_job *jp)
{
if(*jp->jobname != '\0') {
rmlock(Mailqdir,jp->jobname);
}
xfree(jp->from);
del_list(jp->to);
xfree(jp);
}
/* free the message struct and data */
static void near
del_session(struct smtpcli *cb)
{
struct smtpcli *sm, *smtmp = NULLSMTPCLI;
struct smtp_job *jp, *jptmp = NULLJOB;
for(sm = Smtpcli; sm != NULLSMTPCLI; smtmp = sm, sm = sm->next) {
if(sm == cb) {
break;
}
}
if(sm == NULLSMTPCLI) {
/* Not found */
return;
}
/* Remove from list */
if(smtmp != NULLSMTPCLI) {
smtmp->next = sm->next;
} else {
Smtpcli = sm->next;
}
xfree(cb->destname);
xfree(cb->tname);
xfree(cb->wname);
xfree(cb->buf);
for(jp = cb->jobq; jp != NULLJOB; jp = jptmp) {
jptmp = jp->next;
del_job(jp);
}
del_list(cb->errlog);
xfree(cb);
Smtpsessions--; /* number of connections active */
}
/* called to advance to the next job */
static int near
next_job(struct smtpcli *cb)
{
struct smtp_job *jp = cb->jobq->next;
del_job(cb->jobq);
/* remove the error log of previous message */
del_list(cb->errlog);
cb->errlog = NULLLIST;
cb->jobq = jp;
if (jp == NULLJOB) {
return 0;
}
sprintf(cb->tname,"%s/%s.txt",Mailqdir,jp->jobname);
sprintf(cb->wname,"%s/%s.wrk",Mailqdir,jp->jobname);
#ifdef SMTPTRACE
if (Smtptrace) {
tprintf("SMTP sending job %s\n",jp->jobname);
}
#endif
return 1;
}
/* add this job to control block queue */
static struct smtp_job * near
setupjob(struct smtpcli *cb,char *id,char *from)
{
struct smtp_job *p2, *p1 = mxallocw(sizeof(struct smtp_job));
strcpy(p1->jobname,id);
p1->from = strxdup(from);
/* now add to end of jobq */
if ((p2 = cb->jobq) == NULLJOB) {
cb->jobq = p1;
} else {
while(p2->next != NULLJOB) {
p2 = p2->next;
}
p2->next = p1;
}
return p1;
}
/* stub for calling mdaemon to return message to sender */
static void near
retmail(struct smtpcli *cb)
{
FILE *infile;
#ifdef SMTPTRACE
if(Smtptrace) {
tprintf("SMTP job %s returned to sender\n",cb->wname);
}
#endif
if((infile = Fopen(cb->tname,READ_TEXT,0,0)) != NULLFILE) {
mdaemon(infile,cb->jobq->from,cb->errlog,1);
Fclose(infile);
}
}
/* save line in error list */
static void near
logerr(struct smtpcli *cb,char *line)
{
struct list *lp, *tp = mxallocw(sizeof(struct list));
tp->val = strxdup(line);
/* find end of list */
if ((lp = cb->errlog) == NULLLIST) {
cb->errlog = tp;
} else {
while(lp->next != NULLLIST) {
lp = lp->next;
}
lp->next = tp;
}
}
/* Wait for, read and display response from server.
* Return the result code.
* Keep reading until at least this code comes back.
*/
static int near
getresp(struct smtpcli *cb,int mincode)
{
int rval = -1;
char line[LINELEN];
usflush(cb->s);
for(;;){
/* Get line */
if(recvline(cb->s,line,sizeof(line)) == -1) {
break;
}
rip(line); /* Remove cr/lf */
rval = atoi(line);
#ifdef SMTPTRACE
if(Smtptrace) {
tprintf("SMTP recv: %s\n",line);/* Display to user */
}
#endif
if(rval >= 500) { /* Save permanent error replies */
char tmp[LINELEN];
if(cb->errlog == NULLLIST) {
sprintf(tmp,"While talking to %s:",cb->destname);
logerr(cb,tmp);
}
if(cb->buf[0] != '\0') { /* Save offending command */
rip(cb->buf);
sprintf(tmp,">>> %s",cb->buf);
logerr(cb,tmp);
cb->buf[0] = '\0';
}
sprintf(tmp,"<<< %s",line);
logerr(cb,tmp); /* save the error reply */
}
/* Messages with dashes are continued */
if(line[3] != '-' && rval >= mincode) {
break;
}
}
return rval;
}
/* This is the master state machine that handles a single SMTP transaction.
* It is called with a queue of jobs for a particular host.
* The logic is complicated by the "Smtpbatch" variable, which controls
* the batching of SMTP commands. If Smtpbatch is true, then many of the
* SMTP commands are sent in one swell foop before waiting for any of
* the responses. Unfortunately, this breaks many brain-damaged SMTP servers
* out there, so provisions have to be made to operate SMTP in lock-step mode.
*/
static void
smtp_send(int unused,void *cb1,void *p)
{
struct list *tp;
struct sockaddr_in fsocket;
char *cp;
int smtpbatch, rcode, rcpts, goodrcpt, i, init = 1;
struct smtpcli *cb = (struct smtpcli *)cb1;
cb->lock = 1;
fsocket.sin_family = AF_INET;
fsocket.sin_addr.s_addr = cb->ipdest;
fsocket.sin_port = IPPORT_SMTP;
if((cb->s = socket(AF_INET,SOCK_STREAM,0)) == -1) {
tputs(Nosocket);
return;
}
#ifdef SMTPTRACE
if(Smtptrace) {
tprintf("SMTP client Trying %s...\n",psocket((struct sockaddr *)&fsocket));
}
#endif
if(connect(cb->s,(char *)&fsocket,SOCKSIZE) == -1) {
if((cp = sockerr(cb->s)) == NULLCHAR) {
cp = "EOF";
}
#ifdef SMTPTRACE
if(Smtptrace) {
tprintf("SMTP connect failed: %s\n",cp);
}
#endif
log(cb->s,9983,"SMTP %s Connect failed: %s",psocket((struct sockaddr *)&fsocket),cp);
close_s(cb->s);
del_session(cb);
return;
}
#ifdef SMTPTRACE
if(Smtptrace) {
tputs("SMTP connected\n");
}
#endif
sockmode(cb->s,SOCK_ASCII);
smtpbatch = Smtpbatch;
#ifdef LZW
rcode = getresp(cb,200);
if(rcode == -1 || rcode >= 400) {
goto quit;
}
/* disable LZW to ourselve */
if(Smtplzw && cb->ipdest != Ip_addr) {
char cp[LINELEN];
usprintf(cb->s,"XLZW %d %d\n",Lzwbits,Lzwmode);
if(recvline(cb->s,cp,LINELEN) == -1) {
goto quit;
}
if((rcode = atoi(cp)) == 252) {
lzwinit(cb->s,Lzwbits,Lzwmode);
smtpbatch = 1;
}
}
#else
if(!smtpbatch) {
rcode = getresp(cb,200);
if(rcode == -1 || rcode >= 400) {
goto quit;
}
}
#endif
/* Say HELO */
usprintf(cb->s,"HELO %s\n",Hostname);
if(!smtpbatch){
rcode = getresp(cb,200);
if(rcode == -1 || rcode >= 400) {
goto quit;
}
}
do { /* For each message... */
/* if this file open fails, skip it */
if ((cb->tfile = Fopen(cb->tname,READ_TEXT,0,0)) == NULLFILE) {
continue;
}
/* Send MAIL and RCPT commands */
usprintf(cb->s,"MAIL FROM:<%s>\n",cb->jobq->from);
if(!smtpbatch) {
rcode = getresp(cb,200);
if(rcode == -1 || rcode >= 400) {
goto quit;
}
}
rcpts = 0;
goodrcpt = 0;
for(tp = cb->jobq->to; tp != NULLLIST; tp = tp->next) {
usprintf(cb->s,"RCPT TO:<%s>\n",tp->val);
if(!smtpbatch){
rcode = getresp(cb,200);
if(rcode == -1) {
goto quit;
}
if(rcode >= 400) {
continue;
}
goodrcpt = 1; /* At least one good */
}
rcpts++;
}
/* Send DATA command */
usputs(cb->s,"DATA\n");
if(!smtpbatch) {
rcode = getresp(cb,200);
if(rcode == -1 || rcode >= 400) {
goto quit;
}
}
if(smtpbatch){
/* Now wait for the responses to come back. The first time
* we do this, we wait first for the start banner and
* HELO response. In any case, we wait for the response to
* the MAIL command here.
*/
#ifdef LZW
for(i = init ? 2 : 1; i > 0; i--) {
#else
for(i = init ? 3 : 1; i > 0; i--) {
#endif
rcode = getresp(cb,200);
if(rcode == -1 || rcode >= 400) {
goto quit;
}
}
init = 0;
/* Now process the responses to the RCPT commands */
for(i = rcpts; i != 0; i--) {
rcode = getresp(cb,200);
if(rcode == -1) {
goto quit;
}
if(rcode < 400) {
goodrcpt = 1; /* At least one good */
}
}
/* And finally get the response to the DATA command.
* Some servers will return failure here if no recipients
* are valid, some won't.
*/
rcode = getresp(cb,200);
if(rcode == -1 || rcode >= 400) {
goto quit;
}
/* check for no good rcpt on the list */
if(goodrcpt == 0) {
usputs(cb->s,".\n"); /* Get out of data mode */
goto quit;
}
}
/* Send the file. This also closes it */
while(fgets(cb->buf,LINELEN,cb->tfile) != NULL) {
/* Escape a single '.' character at the beginning of a line */
if(strcmp(cb->buf,".\n") == 0) {
usputs(cb->s,".");
}
usputs(cb->s,cb->buf);
}
Fclose(cb->tfile);
cb->tfile = NULLFILE;
/* Send the end-of-message command */
usprintf(cb->s,"%s.\n",(cb->buf[strlen(cb->buf)-1] == '\n') ? "" : "\n");
/* Wait for the OK response */
if((rcode = getresp(cb,200)) == -1) {
goto quit;
}
if((rcode >= 200 && rcode < 300) || rcode >= 500){
/* if a good transfer or permanent failure remove job */
if(cb->errlog != NULLLIST) {
retmail(cb);
}
unlink(cb->tname);
unlink(cb->wname);
log(cb->s,IPPORT_SMTP,
"SMTP sent job %s To: %s From: %s",
cb->jobq->jobname,cb->jobq->to->val,cb->jobq->from);
}
} while(next_job(cb));
quit:
usputs(cb->s,"QUIT\n");
if(cb->errlog != NULLLIST){
retmail(cb);
unlink(cb->wname); /* unlink workfile */
unlink(cb->tname); /* unlink textfile */
}
if(cb->tfile != NULLFILE) {
Fclose(cb->tfile);
}
close_s(cb->s);
del_session(cb);
}
/* This is the routine that gets called every so often to do outgoing
* mail processing. When called with a null argument, it runs the entire
* queue; if called with a specific non-zero IP address from the remote
* kick server, it only starts up sessions to that address.
*/
int
smtptick(void *t)
{
struct smtpcli *cb;
struct smtp_job *jp;
#ifdef SMTPTRACE
struct list *ap;
#endif
char fstring[MAXPATH], from[LINELEN], to[LINELEN], tmpstring[LINELEN];
char wfilename[13], prefix[9], *cp, *cp1;
FILE *wfile;
int32 destaddr, target = (int32)t;
#ifdef SMTPTRACE
if (Smtptrace) {
tprintf("SMTP daemon entered, target = %s\n",inet_ntoa(target));
}
#endif
if(availmem() == 0) {
/* Memory is tight, don't do anything */
return 0;
}
sprintf(fstring,"%s/*.wrk",Mailqdir);
for(filedir(fstring,0,wfilename); *wfilename != '\0'; filedir(fstring,1,wfilename)) {
pwait(NULL);
/* save the prefix of the file name which it job id */
cp = wfilename;
cp1 = prefix;
while(*cp && *cp != '.') {
*cp1++ = *cp++;
}
*cp1 = '\0';
/* lock this file from the smtp daemon */
if(mlock(Mailqdir,prefix)) {
continue;
}
sprintf(tmpstring,"%s/%s",Mailqdir,wfilename);
if((wfile = Fopen(tmpstring,READ_TEXT,0,0)) == NULLFILE) {
/* probably too many open files */
/* continue to next message. The failure may be temporary */
rmlock(Mailqdir,prefix);
continue;
}
fgets(tmpstring,sizeof(tmpstring),wfile); /* read 'target host' */
rip(tmpstring);
fgets(from,sizeof(from),wfile); /* read 'from' */
rip(from);
fgets(to,sizeof(to),wfile); /* read 'to' */
rip(to);
if((destaddr = mailroute(tmpstring)) == 0) {
int perms;
if((perms = userlogin(IPPORT_SMTP,0,to)) == 0) {
if((perms = Smtpbbs) == 0) {
perms = MAIL;
}
}
if(perms == BBS || perms == NEWS) {
destaddr = Ip_addr;
} else {
Fclose(wfile);
rmlock(Mailqdir,prefix);
tprintf(Badhost,tmpstring);
continue;
}
}
if(target && destaddr != target) {
Fclose(wfile);
rmlock(Mailqdir,prefix);
continue;
}
if(!testdelv(destaddr)) {
Fclose(wfile);
rmlock(Mailqdir,prefix);
continue;
}
for(cb = Smtpcli; cb != NULLSMTPCLI; cb = cb->next) {
if(cb->ipdest == destaddr) {
break;
}
}
if(cb == NULLSMTPCLI) {
/* there are enough processes running already */
if(Smtpsessions >= Smtpmaxcli) {
#ifdef SMTPTRACE
if (Smtptrace) {
tputs("SMTP: too many processes\n");
}
#endif
Fclose(wfile);
rmlock(Mailqdir,prefix);
break;
}
cb = mxallocw(sizeof(struct smtpcli));
cb->next = Smtpcli;
Smtpcli = cb;
cb->buf = mxallocw(LINELEN);
cb->wname = mxallocw(strlen(Mailqdir) + JOBNAME + 2);
cb->tname = mxallocw(strlen(Mailqdir) + JOBNAME + 2);
cb->ipdest = destaddr;
cb->destname = strxdup(tmpstring);
Smtpsessions++; /* number of connections active */
} else {
if(cb->lock) {
/* This system is already is sending mail lets not
* interfere with its send queue.
*/
Fclose(wfile);
rmlock(Mailqdir,prefix);
continue;
}
}
if((jp = setupjob(cb,prefix,from)) == NULLJOB) {
Fclose(wfile);
rmlock(Mailqdir,prefix);
del_session(cb);
break;
}
do {
rip(to);
if(addlist(&jp->to,to,DOMAIN) == NULLLIST) {
del_session(cb);
break;
}
} while(fgets(to,sizeof(to),wfile) != NULL);
Fclose(wfile);
#ifdef SMTPTRACE
if (Smtptrace) {
for(ap = jp->to; ap != NULLLIST; ap = ap->next) {
tprintf("SMTP queue job %s From: %s To: %s\n",
prefix,from,ap->val);
}
}
#endif
}
/* start sending that mail */
for(cb = Smtpcli; cb != NULLSMTPCLI && !cb->lock; cb = cb->next) {
sprintf(cb->tname,"%s/%s.txt",Mailqdir,cb->jobq->jobname);
sprintf(cb->wname,"%s/%s.wrk",Mailqdir,cb->jobq->jobname);
newproc("SMTP send",2048,smtp_send,0,cb,NULL,0);
#ifdef SMTPTRACE
if (Smtptrace) {
tprintf("SMTP Trying Connection to %s\n",inet_ntoa(cb->ipdest));
}
#endif
}
return 0;
}
/* ----------------------- SMTP Client subcmds ----------------------- */
static int
dosmtpbatch(int argc,char **argv,void *p)
{
return setbool(&Smtpbatch,"SMTP batch",argc,argv);
}
static int
dosmtpbbs(int argc,char **argv,void *p)
{
return setbool(&Smtpbbs,"SMTP to BBS",argc,argv);
}
static int
dosmtpgate(int argc,char **argv,void *p)
{
int32 n = 0;
if(argc < 2) {
tprintf("SMTP gateway: %s\n",inet_ntoa(Gateway));
} else if((n = resolve(argv[1])) == 0){
tprintf(Badhost,argv[1]);
return 1;
} else {
Gateway = n;
}
return 0;
}
static int
dosmtpkick(int argc,char **argv,void *p)
{
int32 addr = 0;
if(argc > 1 && (addr = resolve(argv[1])) == 0){
tprintf(Badhost,argv[1]);
return 1;
}
smtptick((void *)addr);
return 0;
}
/* kill a job in the mqueue */
static int
dosmtpkill(int argc,char **argv,void *p)
{
char c, *cp, s[LINELEN];
sprintf(s,"%s/%s.lck",Mailqdir,argv[1]);
cp = strrchr(s,'.');
if(access(s,0) == 0) {
Current->ttystate.echo = Current->ttystate.edit = 0;
c = keywait("Warning, the job is locked by SMTP. Remove (y/n)? ",0);
Current->ttystate.echo = Current->ttystate.edit = 1;
if(c != 'y') {
return 0;
}
unlink(s);
}
strcpy(cp,".wrk");
unlink(s);
strcpy(cp,".txt");
unlink(s);
return 0;
}
/* list jobs wating to be sent in the mqueue */
static int
dosmtplist(int argc,char **argv,void *p)
{
char fstring[MAXPATH], tstring[MAXPATH], line[20], host[80];
char from[80], to[80], status, *cp;
struct stat stbuf;
struct tm *tminfo;
FILE *fp;
/* Enable the more mechanism */
Current->flowmode = Cooked;
tputs("S Job Size Date Time Host From\n");
sprintf(fstring,"%s/*.wrk",Mailqdir);
filedir(fstring,0,line);
while(*line != '\0') {
sprintf(tstring,"%s/%s",Mailqdir,line);
if((fp = Fopen(tstring,READ_TEXT,0,1)) == NULLFILE) {
continue;
}
if((cp = strrchr(line,'.')) != NULLCHAR) {
*cp = '\0';
}
sprintf(tstring,"%s/%s.lck",Mailqdir,line);
status = (access(tstring,0)) ? ' ' : 'L';
sprintf(tstring,"%s/%s.txt",Mailqdir,line);
stat(tstring,&stbuf);
tminfo = localtime(&stbuf.st_ctime);
fgets(host,sizeof(host),fp);
rip(host);
fgets(from,sizeof(from),fp);
tprintf("%c %7s %7ld %02d/%02d %02d:%02d %-16.16s %s",
status,
line,
stbuf.st_size,
tminfo->tm_mon+1,
tminfo->tm_mday,
tminfo->tm_hour,
tminfo->tm_min,
host,
from);
while(fgets(to,sizeof(to),fp) != NULLCHAR) {
tprintf(" To: %s",to);
}
Fclose(fp);
filedir(fstring,1,line);
}
Current->flowmode = Raw;
return 0;
}
static int
dosmtpmaxcli(int argc,char **argv,void *p)
{
return setintrc(&Smtpmaxcli,"SMTP maxcli",argc,argv,1,MAXSESSIONS);
}
static int
dosmtpmode(int argc,char **argv,void *p)
{
if (argc < 2) {
tprintf("SMTP mode: %s\n",Smtpmode ? "Queue" : "Route");
} else {
switch(*argv[1]) {
case 'q':
Smtpmode = QUEUE;
break;
case 'r':
Smtpmode = ROUTE;
break;
default:
tputs("Usage: smtp mode [queue|route]\n");
break;
}
}
return 0;
}
static int
dosmtpquiet(int argc,char **argv,void *p)
{
return setintrc(&Smtpquiet,"SMTP quiet",argc,argv,0,3);
}
#ifdef LZW
static int
dosmtplzw(int argc,char **argv,void *p)
{
return setbool(&Smtplzw,"SMTP LZW",argc,argv);
}
#endif
#ifdef SMTPTRACE
static int
dosmtptrace(int argc,char **argv,void *p)
{
return setbool(&Smtptrace,"SMTP tracing",argc,argv);
}
#endif
int
dosmtp(int argc,char **argv,void *p)
{
struct cmds Smtpcmds[] = {
"batch", dosmtpbatch, 0, 0, NULLCHAR,
"bbs", dosmtpbbs, 0, 0, NULLCHAR,
"delete", dosmtpkill, 0, 2, "smtp delete <jobnumber>",
"gateway", dosmtpgate, 0, 0, NULLCHAR,
"kick", dosmtpkick, 0, 0, NULLCHAR,
"list", dosmtplist, 0, 0, NULLCHAR,
#ifdef LZW
"lzw", dosmtplzw, 0, 0, NULLCHAR,
#endif
"maxclient", dosmtpmaxcli, 0, 0, NULLCHAR,
"mode", dosmtpmode, 0, 0, NULLCHAR,
"quiet", dosmtpquiet, 0, 0, NULLCHAR,
#ifdef SMTPTRACE
"trace", dosmtptrace, 0, 0, NULLCHAR,
#endif
NULLCHAR,
};
return subcmd(Smtpcmds,argc,argv,p);
}